#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdbool.h>

#define PROCESS_NUM 10
#define MAXEVENTS 64

static int createAndBind(int port)
{
	int fd = socket(PF_INET, SOCK_STREAM, 0);

	int val = 1;
	setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));

	struct sockaddr_in serveraddr;
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	serveraddr.sin_port = htons(port);

	bind(fd, (struct sockaddr*)&serveraddr, sizeof(struct sockaddr_in));
	return fd;
}

static int makeNonBlocking(int sfd)
{
	int flags = fcntl(sfd, F_GETFL, 0);
	flags |= O_NONBLOCK;
	fcntl(sfd, F_SETFL, flags);

	return 0;
}

int main(int argc, char *argv[])
{
	int sfd = createAndBind(8001);
	makeNonBlocking(sfd);
	listen(sfd, SOMAXCONN);

	int efd = epoll_create(MAXEVENTS);

	struct epoll_event event;
	event.data.fd = sfd;
	//event.events = EPOLLIN | EPOLLET;
	// EPOLLEXCLUSIVE 和 EPOLLONESHOT 不能同时使用
	event.events = EPOLLIN | EPOLLONESHOT;

	int ret = epoll_ctl(efd, EPOLL_CTL_ADD, sfd, &event);
	if (ret == -1)
	{
		perror("epoll_ctl");
		abort();
	}

	/* Buffer where events are returned */
	struct epoll_event* events = calloc(MAXEVENTS, sizeof(struct epoll_event));
	for (int k = 0; k < PROCESS_NUM; k++)
	{
		int pid = fork();
		if (pid != 0)
		{
			// In parent
			continue;
		}

		/* In child: The event loop */
		while (true)
		{
			int n = epoll_wait(efd, events, MAXEVENTS, -1);
			printf("process %d return from epoll_wait! n = %d\n", getpid(), n);

			for (int i = 0; i < n; i++)
			{
				if ((events[i].events & EPOLLERR)
					|| (events[i].events & EPOLLHUP)
					|| (!(events[i].events & EPOLLIN)))
				{
					/* An error has occured on this fd, or the socket is not ready for reading(why were we notified then?) */
					fprintf(stderr, "epoll error\n");
					close(events[i].data.fd);
					continue;
				}
				else if (sfd == events[i].data.fd)
				{
					/* We have a notification on the listening socket, which means one or more incoming connections. */
					struct sockaddr in_addr;
					char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

					socklen_t in_len = sizeof(struct sockaddr);
					int infd = accept(sfd, &in_addr, &in_len);
					if (infd == -1)
					{
						printf("process %d accept failed!\n", getpid());
						break;
					}
					printf("process %d accept successed!\n", getpid());

					// close(infd);
				}
			}
		}
	}

	int status;
	wait(&status);

	free(events);
	close(sfd);
	return 0;
}